home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / mxutil / tspak17 / recorder.asm < prev    next >
Assembly Source File  |  1994-09-09  |  15KB  |  587 lines

  1. ; RECORDER.ASM
  2. ;
  3. ; This file contains external Turbo Pascal subroutines for asynchronous
  4. ; sound recording on the Tandy DAC.  The Tandy DAC is assumed to be present;
  5. ; its address is obtained from the BIOS.
  6. ;
  7. ; To use this package, the main program calls start_record() to start, then
  8. ; writes out data as fullbuf flags become true, then calls stop_record() to
  9. ; stop recording.  The main program should set the fullbuf flag off after
  10. ; writing it to disk or whatever; if the fullbuf flag is still set when the
  11. ; recording package gets back to the buffer, ovrflo is set to true and
  12. ; recording stops.  The main program should watch that flag also.  The main
  13. ; program should mark both buffers empty and neither buffer last and clear
  14. ; the ovrflo flag before calling start_record().
  15. ;
  16. ; These routines go directly to the hardware.  The reason for that is to be
  17. ; able to stop recording instantaneously and report the number of bytes
  18. ; recorded into the last buffer.  With Int 1Ah, one can only set the PSSJ
  19. ; to record a fixed number of bytes; although you can stop anytime, you can't
  20. ; tell how much has actually been recorded since the BIOS doesn't report
  21. ; that.
  22. ;
  23. ; start_record() takes two far pointers to buffers as parameters.  The
  24. ; buffers are 32768 bytes in size.  Each buffer is divided into two parts
  25. ; if necessary to ensure that neither part crosses a 64k boundary.
  26. ;
  27. ; start_record() does the following:
  28. ;   converts buffer addresses to linear
  29. ;   divides the buffers so that they do not cross 64k boundaries and
  30. ;     determines the length of each part
  31. ;   gets the DAC address from the BIOS
  32. ;   sets the current input buffer to buffer0
  33. ;   sets the number of bytes done to zero
  34. ;   hooks Int 15h and 0Fh
  35. ;   disables interrupts
  36. ;   programs the DMA controller for sound input on the first part of the
  37. ;     first buffer
  38. ;   programs the sound chip to do input with DMA
  39. ;   starts sound DMA
  40. ;   enables interrupts
  41. ;
  42. ; The Int 0Fh handler does the following:
  43. ;   if not a DMA EOP interrupt, jumps to default Int 0Fh handler
  44. ;   adds the size of the buffer part done to the number of bytes done
  45. ;   if the first part of a two-part buffer:
  46. ;     starts DMA on the second part
  47. ;   otherwise:
  48. ;     sets lastbytes to the number of bytes done (32768)
  49. ;     marks the buffer full
  50. ;     sets the current input buffer to the other buffer
  51. ;     if the current input buffer is marked full then:
  52. ;       sets ovrflo to true
  53. ;     otherwise:
  54. ;       starts DMA on the first part of the current buffer
  55. ;   issues EOI to the interrupt controller
  56. ;
  57. ; The Int 15h handler does the following:
  58. ;   if AH <> 4Fh, jumps to default Int 15h handler
  59. ;   if code in AL is not a make code, clears carry and exits
  60. ;   if sound DMA is not in progress, clears carry and exits
  61. ;   otherwise:
  62. ;     stops sound DMA
  63. ;     adds the current DMA channel 1 count to the number of bytes done
  64. ;     marks the current buffer full
  65. ;     marks the current buffer last
  66. ;     sets lastbytes to the number of bytes done
  67. ;     clears carry
  68. ;
  69. ; stop_record() does the following:
  70. ;   stops sound DMA if in progress
  71. ;   unhooks Int 15h and 0Fh
  72. ;
  73. ; The Turbo Pascal invocation syntax is:
  74. ;
  75. ; procedure start_record(
  76. ;   divider:                   (* DAC divider for recording *)
  77. ;     word;
  78. ;   buffer0,                   (* pointer to first sound DMA buffer *)
  79. ;   buffer1:                   (* pointer to second sound DMA buffer *)
  80. ;     pointer;
  81. ;   var lastbuf,               (* "last buffer" flags *)
  82. ;       fullbuf:               (* "full buffer" flags *)
  83. ;     bool2;
  84. ;   var lastbytes:             (* number of bytes in the last buffer *)
  85. ;     word;
  86. ;   var ovrflo:                (* true if input overflow occurred *)
  87. ;     boolean ); external;
  88. ;
  89. ; procedure stop_record; external;
  90. ;
  91.  
  92. CODE        SEGMENT    BYTE PUBLIC
  93.         ASSUME    CS:CODE,DS:CODE
  94.         PUBLIC    START_RECORD,STOP_RECORD
  95.  
  96. ;
  97. ; Parameters for start_record().
  98. ;
  99. OVRFLO        EQU    [BP+4]
  100. LASTBYTES    EQU    [BP+8]
  101. FULLBUF        EQU    [BP+12]
  102. LASTBUF        EQU    [BP+16]
  103. BUFFER1        EQU    [BP+20]
  104. BUFFER0        EQU    [BP+24]
  105. DIVIDER        EQU    [BP+28]
  106.  
  107. ;
  108. ; Local variables.
  109. ;
  110. OVRFLOPTR    DD    0    ; pointer to overflow flag (byte)
  111. LASTBYTESPTR    DD    0    ; pointer to lastbytes count (word)
  112. FULLBUFPTR    DD    0    ; pointer to "full buffer" flag array (2 bytes)
  113. LASTBUFPTR    DD    0    ; pointer to "last buffer" flag array (2 bytes)
  114.         ;
  115.         ; "Buffers" in this package are DMA buffers, up to four of
  116.         ; them.  Page value of 0 indicates not used.  Two of these
  117.         ; buffers equal one for the main program.
  118.         ;
  119. CURRENTBUF    DW    0        ; current buffer (0-3)
  120. BUFADDRS    DW    4 DUP (0)    ; buffer start addresses for DMA
  121. BUFPAGES    DB    4 DUP (0)    ; buffer page register values for DMA
  122. BUFCOUNTS    DW    4 DUP (0)    ; buffer initial counts for DMA
  123.         ;
  124.         ; Some more locals.
  125.         ;
  126. DACADDR        DW    0        ; base I/O port address of DAC
  127. INT15DEFAULT    DD    0        ; default Int 15h vector
  128. INT0FDEFAULT    DD    0        ; default Int 0Fh vector
  129. INTMASK        DB    0        ; default 8259A interrupt mask
  130.         ;
  131.         ; Number of bytes recorded since the last buffer was marked
  132.         ; full.
  133.         ;
  134. BYTESDONE    DW    0
  135.  
  136. ;
  137. ; Int 0Fh service routine (DMA EOP).
  138. ;
  139. ; If not an end-of-process interrupt, jump to the default handler.
  140. ;
  141. INT0FHDLR    PROC    FAR
  142.         PUSH    AX
  143.         PUSH    DX
  144.         MOV    DX,CS:DACADDR
  145.         IN    AL,DX
  146.         TEST    AL,8
  147.         JNZ    INT0F_EOP
  148.         POP    DX
  149.         POP    AX
  150.         JMP    DWORD PTR CS:INT0FDEFAULT
  151.         ;
  152.         ; DMA EOP on channel 1.
  153.         ;
  154. INT0F_EOP:    CLI
  155.         PUSH    BX
  156.         PUSH    SI
  157.         PUSH    DI
  158.         PUSH    DS
  159.         PUSH    ES
  160.         MOV    AX,CS        ; DS addresses local data
  161.         MOV    DS,AX
  162.         MOV    AL,5        ; disable DMA channel 1
  163.         OUT    0Ah,AL
  164.         MOV    DX,DACADDR    ; clear interrupt
  165.         IN    AL,DX
  166.         JMP    $+2
  167.         AND    AL,0F7h
  168.         OUT    DX,AL
  169.         ;
  170.         ; Add count for buffer to number of bytes done.
  171.         ;
  172.         MOV    DI,CURRENTBUF    ; get array index
  173.         SHL    DI,1
  174.         MOV    AX,BUFCOUNTS[DI]
  175.         INC    AX
  176.         ADD    AX,BYTESDONE
  177.         MOV    BYTESDONE,AX
  178.         ;
  179.         ; If 32768 bytes have been done, mark the buffer full.
  180.         ;
  181.         CMP    AX,32768
  182.         JB    INT0F_NOTDONE
  183.         SHR    DI,1        ; DI = buffer0, buffer1
  184.         SHR    DI,1
  185.         LES    BX,FULLBUFPTR
  186.         MOV    BYTE PTR ES:[BX+DI],1
  187.         XOR    DI,1        ; go to next pair of buffers
  188.         CMP    BYTE PTR ES:[BX+DI],1    ; check for overflow
  189.         JNE    INT0F_NOOVRFLO
  190.         ;
  191.         ; Overflow occurred.  Mark last buffer last and set ovrflo
  192.         ; flag.
  193.         ;
  194.         XOR    DI,1        ; go back to buffer that overflowed
  195.         LES    BX,LASTBUFPTR        ; mark it last
  196.         MOV    BYTE PTR ES:[BX+DI],1
  197.         LES    BX,LASTBYTESPTR        ; set LASTBYTES to 32768
  198.         MOV    WORD PTR ES:[BX],32768
  199.         LES    BX,OVRFLOPTR        ; set overflow flag
  200.         MOV    BYTE PTR ES:[BX],1
  201.         MOV    DX,DACADDR        ; stop sound DMA
  202.         IN    AL,DX
  203.         JMP    $+2
  204.         AND    AL,0FBh
  205.         OUT    DX,AL
  206.         JMP    INT0F_EXIT
  207.         ;
  208.         ; Overflow did not occur.  Set CURRENTBUF to address the
  209.         ; next pair of buffers.
  210.         ;
  211. INT0F_NOOVRFLO:    SHL    DI,1
  212.         MOV    CURRENTBUF,DI
  213.         MOV    SI,DI
  214.         SHL    DI,1
  215.         MOV    BYTESDONE,0
  216.         JMP    INT0F_RESTART
  217.         ;
  218.         ; Need to do the second part of the buffer pair.
  219.         ;
  220. INT0F_NOTDONE:    MOV    SI,CURRENTBUF
  221.         INC    SI
  222.         MOV    CURRENTBUF,SI
  223.         MOV    DI,SI
  224.         SHL    DI,1
  225.         ;
  226.         ; Restart DMA on the new buffer half.  SI is a byte array
  227.         ; index, DI is a word array index.
  228.         ;
  229. INT0F_RESTART:    MOV    AL,45h    ; select channel 1, write transfer to memory,
  230.         OUT    0Bh,AL    ;   autoinitialization disabled, address incre-
  231.         JMP    $+2    ;   ment, single mode
  232.         MOV    AL,BUFPAGES[SI]
  233.         OUT    83h,AL        ; set DMA channel 1 page register
  234.         JMP    $+2
  235.         MOV    AL,0FFh        ; clear byte pointer flip/flop
  236.         OUT    0Ch,AL
  237.         JMP    $+2
  238.         MOV    AX,BUFCOUNTS[DI]
  239.         OUT    03h,AL        ; set DMA channel 1 count
  240.         JMP    $+2
  241.         MOV    AL,AH
  242.         OUT    03h,AL
  243.         JMP    $+2
  244.         MOV    AX,BUFADDRS[DI]
  245.         OUT    02h,AL        ; set DMA channel 1 base address
  246.         JMP    $+2
  247.         MOV    AL,AH
  248.         OUT    02h,AL
  249.         MOV    DX,DACADDR    ; reenable sound chip EOP interrupt
  250.         IN    AL,DX
  251.         JMP    $+2
  252.         OR    AL,8
  253.         OUT    DX,AL
  254.         MOV    AL,1        ; enable DMA channel 1
  255.         OUT    0Ah,AL
  256.         ;
  257.         ; Issue EOI, restore registers and exit.
  258.         ;
  259. INT0F_EXIT:    MOV    AL,20h
  260.         OUT    20h,AL
  261.         POP    ES
  262.         POP    DS
  263.         POP    DI
  264.         POP    SI
  265.         POP    BX
  266.         ;
  267.         POP    DX
  268.         POP    AX
  269.         IRET
  270. INT0FHDLR    ENDP
  271.  
  272. ;
  273. ; Int 15h service routine (keyboard intercept).
  274. ;
  275. ; If not a keyboard intercept, jump to default handler.
  276. ;
  277. INT15HDLR    PROC    FAR
  278.         CMP    AH,4Fh
  279.         JNE    INT15_INTRCEPT
  280.         JMP    DWORD PTR CS:INT15DEFAULT
  281.         ;
  282.         ; If not a make code, clear carry and exit.
  283.         ;
  284. INT15_INTRCEPT:    TEST    AL,80h
  285.         JZ    INT15_MAKE
  286.         JMP    INT15_EXIT
  287.         ;
  288.         ; It's a make code.  Check if sound DMA is in progress.  If
  289.         ; not, clear carry and exit.
  290.         ;
  291. INT15_MAKE:    PUSH    AX
  292.         PUSH    DX
  293.         MOV    DX,CS:DACADDR
  294.         IN    AL,DX
  295.         TEST    AL,4
  296.         JNZ    INT15_INDMA
  297.         JMP    INT15_POPDX
  298.         ;
  299.         ; Sound DMA in progress.  Stop it.
  300.         ;
  301. INT15_INDMA:    CLI
  302.         PUSH    BX        ; save other registers needed
  303.         PUSH    SI
  304.         PUSH    DI
  305.         PUSH    DS
  306.         PUSH    ES
  307.         AND    AL,0FBh        ; disable sound chip DMA
  308.         OUT    DX,AL
  309.         MOV    AX,CS        ; DS addresses local data
  310.         MOV    DS,AX
  311.         MOV    SI,CURRENTBUF    ; SI addresses caller's arrays
  312.         MOV    DI,SI        ; DI is word array index
  313.         SHR    SI,1
  314.         SHL    DI,1
  315.         MOV    AL,5    ; disable DMA channel 1 while programming it
  316.         OUT    0Ah,AL
  317.         JMP    $+2
  318.         MOV    AL,0FFh        ; clear byte pointer flip/flop
  319.         OUT    0Ch,AL
  320.         JMP    $+2
  321.         IN    AL,3        ; get DMA channel 1 count
  322.         JMP    $+2
  323.         MOV    AH,AL
  324.         IN    AL,3
  325.         XCHG    AL,AH        ; AX = DMA channel 1 count (current)
  326.         SUB    AX,BUFCOUNTS[DI]
  327.         NEG    AX        ; AX = number of bytes done since EOP
  328.         ADD    AX,BYTESDONE    ; AX = number of bytes in buffer0/1
  329.         LES    BX,LASTBYTESPTR    ; set # of bytes in last buffer
  330.         MOV    ES:[BX],AX
  331.         LES    BX,LASTBUFPTR    ; mark buffer last
  332.         MOV    BYTE PTR ES:[BX+SI],1
  333.         LES    BX,FULLBUFPTR    ; mark buffer full
  334.         MOV    BYTE PTR ES:[BX+SI],1
  335.         POP    ES
  336.         POP    DS
  337.         POP    DI
  338.         POP    SI
  339.         POP    BX
  340.         STI
  341.         ;
  342.         ; Pop DX, AX.
  343.         ;
  344. INT15_POPDX:    POP    DX
  345.         POP    AX
  346.         ;
  347.         ; Clear carry and exit (thereby causing the BIOS to ignore
  348.         ; the keystroke).
  349.         ;
  350. INT15_EXIT:    CLC
  351.         RETF    2
  352. INT15HDLR    ENDP
  353.  
  354. ;
  355. ; Subroutine, converts pointer in DX:AX to a linear address.  Modifies AX,
  356. ; CX,DX.
  357. ;
  358. TO_LINEAR    PROC    NEAR
  359.         MOV    CL,4
  360.         ROL    DX,CL
  361.         MOV    CX,DX
  362.         AND    CX,0FFF0h
  363.         AND    DX,0Fh
  364.         ADD    AX,CX
  365.         ADC    DX,0
  366.         RET
  367. TO_LINEAR    ENDP
  368.  
  369. ;
  370. ; Subroutine, takes the linear address of a buffer in DL:AX and its number
  371. ; (0 or 2) in SI, and fills in the buffer data arrays.  Modifies AX,DX,SI,DI.
  372. ;
  373. FIX_64K        PROC    NEAR
  374.         MOV    DI,SI
  375.         SHL    DI,1
  376.         MOV    BUFADDRS[DI],AX        ; save address of first part
  377.         MOV    BUFPAGES[SI],DL
  378.         ;
  379.         ; If buffer is contained in one 64k segment, the second part
  380.         ; is not used, and 32768 bytes are recorded into the first
  381.         ; part.
  382.         ;
  383.         CMP    AX,32768
  384.         JA    FIX_64K_HI
  385.         MOV    BUFCOUNTS[DI],32767    ; DMA transfer 32768 bytes
  386.         MOV    BUFPAGES[SI+1],0    ; zero page indicates not used
  387.         RET
  388.         ;
  389.         ; Otherwise, the buffer is split into two parts.  The first
  390.         ; part extends to the 64k boundary.
  391.         ;
  392. FIX_64K_HI:    NOT    AX            ; = (65536 - AX) - 1
  393.         MOV    BUFCOUNTS[DI],AX
  394.         MOV    BUFADDRS[DI+2],0    ; offset 0 in next DMA page
  395.         INC    DL
  396.         MOV    BUFPAGES[SI+1],DL    ; next DMA page
  397.         SUB    AX,32766        ; = (32768 - (AX + 1)) - 1
  398.         NEG    AX
  399.         MOV    BUFCOUNTS[DI+2],AX
  400.         RET
  401. FIX_64K        ENDP
  402.  
  403. ;
  404. ; Start routine.
  405. ;
  406. START_RECORD    PROC    NEAR
  407.         PUSH    BP        ; save stack pointer
  408.         MOV    BP,SP        ; address parameters on stack
  409.         PUSH    DS        ; save DS
  410.         MOV    AX,CS        ; DS addresses local data
  411.         MOV    DS,AX
  412.         ;
  413.         ; Copy pointers from stack to code segment for use by the
  414.         ; interrupt handlers.
  415.         ;
  416.         LES    AX,OVRFLO
  417.         MOV    WORD PTR OVRFLOPTR,AX
  418.         MOV     WORD PTR OVRFLOPTR+2,ES
  419.         LES    AX,LASTBYTES
  420.         MOV    WORD PTR LASTBYTESPTR,AX
  421.         MOV    WORD PTR LASTBYTESPTR+2,ES
  422.         LES    AX,FULLBUF
  423.         MOV    WORD PTR FULLBUFPTR,AX
  424.         MOV    WORD PTR FULLBUFPTR+2,ES
  425.         LES    AX,LASTBUF
  426.         MOV    WORD PTR LASTBUFPTR,AX
  427.         MOV    WORD PTR LASTBUFPTR+2,ES
  428.         ;
  429.         ; Get buffer0 pointer, convert to linear, and set buffer
  430.         ; array data.
  431.         ;
  432.         MOV    AX,BUFFER0
  433.         MOV    DX,BUFFER0+2
  434.         CALL    TO_LINEAR
  435.         XOR    SI,SI
  436.         CALL    FIX_64K
  437.         ;
  438.         ; Same deal with buffer1.
  439.         ;
  440.         MOV    AX,BUFFER1
  441.         MOV    DX,BUFFER1+2
  442.         CALL    TO_LINEAR
  443.         MOV    SI,2
  444.         CALL    FIX_64K
  445.         ;
  446.         ; Get the base DAC port address and save it.
  447.         ;
  448.         MOV    AX,8100h
  449.         INT    1Ah
  450.         MOV    DACADDR,AX
  451.         ;
  452.         ; Set first buffer to 0, number of bytes recorded to 0.
  453.         ;
  454.         XOR    AX,AX
  455.         MOV    CURRENTBUF,AX
  456.         MOV    BYTESDONE,AX
  457.         ;
  458.         ; Hook Int 0Fh and 15h.
  459.         ;
  460.         MOV    AX,350Fh
  461.         INT    21h
  462.         MOV    WORD PTR INT0FDEFAULT,BX
  463.         MOV    WORD PTR INT0FDEFAULT+2,ES
  464.         MOV    AX,3515h
  465.         INT    21h
  466.         MOV    WORD PTR INT15DEFAULT,BX
  467.         MOV    WORD PTR INT15DEFAULT+2,ES
  468.         MOV    AX,250Fh
  469.         MOV    DX,OFFSET INT0FHDLR
  470.         INT    21h
  471.         MOV    AX,2515h
  472.         MOV    DX,OFFSET INT15HDLR
  473.         INT    21h
  474.         ;
  475.         ; Disable interrupts.
  476.         ;
  477.         CLI
  478.         ;
  479.         ; Program DMA channel 1.
  480.         ;
  481.         MOV    AL,5    ; disable DMA channel 1 while programming it
  482.         OUT    0Ah,AL
  483.         JMP    $+2
  484.         MOV    AL,45h    ; select channel 1, write transfer to memory,
  485.         OUT    0Bh,AL    ;   autoinitialization disabled, address incre-
  486.         JMP    $+2    ;   ment, single mode
  487.         MOV    AL,BUFPAGES
  488.         OUT    83h,AL    ; set DMA channel 1 page register
  489.         JMP    $+2
  490.         MOV    AL,0FFh    ; clear byte pointer flip/flop
  491.         OUT    0Ch,AL
  492.         JMP    $+2
  493.         MOV    AX,BUFCOUNTS
  494.         OUT    03h,AL    ; set DMA channel 1 count
  495.         JMP    $+2
  496.         MOV    AL,AH
  497.         OUT    03h,AL
  498.         JMP    $+2
  499.         MOV    AX,BUFADDRS
  500.         OUT    02h,AL    ; set DMA channel 1 base address
  501.         JMP    $+2
  502.         MOV    AL,AH
  503.         OUT    02h,AL
  504.         ;
  505.         ; DMA still disabled.  Set up sound chip; will start when
  506.         ; DMA is enabled.
  507.         ;
  508.         MOV    DX,DACADDR
  509.         IN    AL,DX
  510.         JMP    $+2
  511.         AND    AL,0E0h        ; recording with DMA/interrupt
  512.         OR    AL,16h
  513.         OUT    DX,AL
  514.         ADD    DX,2        ; set recording speed - volume setting
  515.         MOV    AX,DIVIDER    ;   ignored when recording
  516.         OUT    DX,AL
  517.         JMP    $+2
  518.         INC    DX
  519.         MOV    AL,AH
  520.         OUT    DX,AL
  521.         JMP    $+2
  522.         MOV    DX,DACADDR    ; enable DMA EOP interrupt
  523.         IN    AL,DX
  524.         JMP    $+2
  525.         AND    AL,0E0h
  526.         OR    AL,1Eh
  527.         OUT    DX,AL
  528.         ;
  529.         ; Save interrupt mask and enable IRQ 7.
  530.         ;
  531.         IN    AL,21h
  532.         JMP    $+2
  533.         MOV    INTMASK,AL
  534.         AND    AL,7Fh
  535.         OUT    21h,AL
  536.         ;
  537.         ; Enable DMA channel 1 to get things going.
  538.         ;
  539.         MOV    AL,1
  540.         OUT    0Ah,AL
  541.         ;
  542.         ; Enable interrupts and exit.
  543.         ;
  544.         STI
  545.         POP    DS        ; restore DS
  546.         POP    BP        ; restore stack pointer
  547.         RET
  548. START_RECORD    ENDP
  549.  
  550. ;
  551. ; Stop routine.  This routine may be called more than once, although calls
  552. ; after the first have no effect.
  553. ;
  554. ; Stop sound chip DMA.
  555. ;
  556. STOP_RECORD    PROC    NEAR
  557.         PUSH    DS
  558.         CLI
  559.         MOV    DX,CS:DACADDR
  560.         IN    AL,DX
  561.         JMP    $+2
  562.         AND    AL,0FBh
  563.         OUT    DX,AL
  564.         STI
  565.         ;
  566.         ; Unhook Int 0Fh and Int 15h.
  567.         ;
  568.         MOV    AX,250Fh
  569.         LDS    DX,CS:INT0FDEFAULT
  570.         INT    21h
  571.         MOV    AX,2515h
  572.         LDS    DX,CS:INT15DEFAULT
  573.         INT    21h
  574.         ;
  575.         ; Restore interrupt mask (disable IRQ 7 if disabled before).
  576.         ;
  577.         CLI
  578.         MOV    AL,CS:INTMASK
  579.         OUT    21h,AL
  580.         JMP    $+2
  581.         STI
  582.         POP    DS
  583.         RET
  584. STOP_RECORD    ENDP
  585. CODE        ENDS
  586.         END
  587.